﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common;
using System.Data.SqlClient;
using System.Configuration;
using Dal.Properties;
using System.Collections;
using System.Data;
using System.Transactions;

namespace Dal
{
    /// <summary>
    /// This class performs catalog functionality using the DB.
    /// </summary>
    public class Catalog
    {
        HebrewStemmer _stemmer;
        string _connectionString;

        public Catalog(string prefixFilePath)
        {
            _connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
            if (_connectionString == null)
                _connectionString = Settings1.Default.ConnectionString;
            _stemmer = new HebrewStemmer(prefixFilePath);

        }

        public void IndexDocumentToCatalog(Document Document)
        {
            //If document was already indexed OR document is empty, abort.
            // if (null != Catalog.GetDocumentByID(Document.Id))
            //     return;
            using (SqlConnection con = new SqlConnection(_connectionString))
            {
                con.Open();

                SqlCommand command = new SqlCommand("add_word", con);
                command.CommandType = System.Data.CommandType.StoredProcedure;


                string[] TextArray;
                for (int i = 0; i < Document.NumberOfFields; i++)
                {
                    TextArray = QueryParser.GetParsedPhraseArray(Document.Fields[i].Text, true);
                    Document.Fields[i].Length = TextArray.Length;
                    for (int j = 0; j < TextArray.Length; j++)
                    {
                        WordPos WordPos = new WordPos();
                        WordPos.Position = j + 1;
                        WordPos.Section = (Document.Section)i;
                        AddWord(new Word(TextArray[j]), Document.ID, WordPos, command);
                        //trans.Commit();
                    }
                    UpdateSumOfFieldLength(i, Document.Fields[i].Length, con);
                }


            }
        }

        public void AddWord(Word word, long DocumentID, WordPos wordPosition, SqlCommand command)
        {
          
                if (word.Text == String.Empty)
                    return;
                WordPos WordPosition = new WordPos(wordPosition);
                List<Word> wordList = GetWordListFromStringList(((string[])_stemmer.GetStemmedWords(word.Text).ToArray()).ToList<string>());
                //wordList.Add(word);

                foreach (Word Word in wordList)
                {
                    if (wordList[0] == Word) //This is original word
                        WordPosition.IsOriginal = true;
                    else
                        WordPosition.IsOriginal = false;

                      command.Parameters.Clear();
                    command.Parameters.Add(new SqlParameter("@word_text", Word.Text));
                    command.Parameters.Add(new SqlParameter("@doc_id", DocumentID));
                    command.Parameters.Add(new SqlParameter("@ds_id", (int)WordPosition.Section));
                    command.Parameters.Add(new SqlParameter("@position", WordPosition.Position));
                    command.Parameters.Add(new SqlParameter("@is_original", WordPosition.IsOriginal));
                    command.ExecuteNonQuery();

                }
            
        }

        private List<Word> GetWordListFromStringList(List<string> StringList)
        {
            List<Word> ret = new List<Word>();
            foreach (string s in StringList)
                ret.Add(new Word(s));
            return ret;
        }

        /// <summary>
        /// For a particular searchword, 
        /// Gets a hash of Key: DocumentID, Value : List of WordPos for that word in the document.
        /// </summary>
        /// <param name="word"></param>
        /// <returns> a hash of Key: DocumentID, Value : List of WordPos for that word in the document.</returns>
        public Hashtable GetDocumentListPerWord(string word)
        {
            return GetDocumentListPerWord(new Word(word), false);
        }

        /// <summary>
        /// For a particular searchword, 
        /// Gets a hash of Key: DocumentID, Value : List of WordPos for that word in the document.
        /// </summary>
        /// <param name="word"></param>
        /// <param name="ExactMatch">should this word be matched exactly or conjugated</param>
        /// <returns> a hash of Key: DocumentID, Value : List of WordPos for that word in the document.</returns>
        public Hashtable GetDocumentListPerWord(string word, bool ExactMatch)
        {
            return GetDocumentListPerWord(new Word(word), ExactMatch);
        }


        private Hashtable GetDocumentListPerWord(Word searchWord, bool ExactMatch)
        {
            Hashtable retval = new Hashtable();
            List<Word> allCombinations = new List<Word>();
         
            if (ExactMatch) //Match only the word requested (no הטיות)
            {
                DataTable dt = GetDocumentsContainingWord(searchWord);
                foreach (DataRow dr in dt.Rows)
                {
                    long doc_id = (long)dr["doc_id"];
                    WordPos w = new WordPos();
                    w.Position = (int)dr["position"];
                    w.Section = (Document.Section)Enum.Parse(typeof(Document.Section), dr["ds_id"].ToString());
                    w.IsOriginal = (bool)dr["is_original"];

                    if (retval.Contains(doc_id)) //Our list already contains this document. 
                    {
                        if (w.IsOriginal)
                            (retval[doc_id] as List<WordPos>).Add(w);
                    }
                    else
                    {

                        List<WordPos> newWordPosList = new List<WordPos>();
                        if (w.IsOriginal)
                            newWordPosList.Add(w);
                        retval.Add(doc_id, newWordPosList);
                    }

                }//foreach word position in doc

            } //If ExactMatch
            else
            {
                allCombinations = GetWordListFromStringList(((string[])_stemmer.GetStemmedWords(searchWord.Text).ToArray()).ToList<string>());

                foreach (Word Word in allCombinations)
                {
                    DataTable dt = GetDocumentsContainingWord(Word);
                    foreach (DataRow dr in dt.Rows)
                    {
                        long doc_id = (long)dr["doc_id"];
                        WordPos w = new WordPos();
                        w.Position = (int)dr["position"];
                        w.Section = (Document.Section)Enum.Parse(typeof(Document.Section), dr["ds_id"].ToString());
                        w.IsOriginal = (bool)dr["is_original"];

                        if (retval.Contains(doc_id)) //Our list already contains this document. 
                        {
                            (retval[doc_id] as List<WordPos>).Add(w);
                        }
                        else
                        {

                            List<WordPos> newWordPosList = new List<WordPos>();
                            newWordPosList.Add(w);
                            retval.Add(doc_id, newWordPosList);
                        }
                    }
                }
            }
            return retval;// (retval.Count == 0) ? null : retval;
        }
        private int MaxDistanceBweteenWords(Word w1, Word w2, long DocId, Document.Section section)
        {
            Hashtable wd1 = GetDocumentListPerWord(w1.Text);
            Hashtable wd2 = GetDocumentListPerWord(w2.Text);

            var wp1 = wd1[DocId] as List<WordPos>;
            var wp2 = wd2[DocId] as List<WordPos>;

            int minW1, minW2, maxW1, maxW2;
            MinMaxPosition(wp1, out minW1, out maxW1, section);
            MinMaxPosition(wp2, out minW2, out maxW2, section);
            return Math.Max((maxW2 - minW1), (maxW1 - minW2));
        }
        private void MinMaxPosition(List<WordPos> wpList, out int min, out int max, Document.Section section)
        {
            min = int.MaxValue;
            max = int.MinValue;
            foreach (WordPos wp in wpList)
            {
                if (wp.Section != section) continue;
                var pos = wp.Position;
                if (pos < min) min = pos;
                if (pos > max) max = pos;
            }
        }

        #region Database Operations

        /// <summary>
        /// Gets a data reader for all documents 
        /// </summary>
        /// <param name="searchword"></param>
        /// <returns></returns>
        private DataTable GetDocumentsContainingWord(Word searchword)
        {

            using (SqlConnection con = new SqlConnection(_connectionString))
            {
                con.Open();
                SqlCommand command = new SqlCommand("get_word_position_in_document", con);
                command.CommandType = System.Data.CommandType.StoredProcedure;
                command.Parameters.Add(new SqlParameter("@word_text", searchword.Text));
                DataTable dt = new DataTable();
                SqlDataAdapter a = new SqlDataAdapter(command);
                a.Fill(dt);
                return dt;

            }
        }

        public int NumberOfDocumentsIndexed()
        {
            using (SqlConnection con = new SqlConnection(_connectionString))
            {
                con.Open();
                SqlCommand command = new SqlCommand("get_number_of_documents_indexed", con);
                command.CommandType = System.Data.CommandType.StoredProcedure;
                return (int)command.ExecuteScalar();
            }
        }

        public Document GetDocumentByID(long doc_id)
        {
            DatabaseAbstractionLayer dal = new DatabaseAbstractionLayer();
            return dal.GetDocumentByID(doc_id);
        }

        public void UpdateSumOfFieldLength(int fieldID, long sumOfFieldLength, SqlConnection con)
        {

                SqlCommand command = new SqlCommand("update_sum_of_field_length", con);
                command.CommandType = System.Data.CommandType.StoredProcedure;
                command.Parameters.Add(new SqlParameter("@field_id", fieldID));
                command.Parameters.Add(new SqlParameter("@length", sumOfFieldLength));
                command.ExecuteNonQuery();
        }

        public long SumOfFieldLength(int fieldID)
        {
            using (SqlConnection con = new SqlConnection(_connectionString))
            {
                con.Open();
                SqlCommand command = new SqlCommand("get_sum_of_field_length", con);
                command.CommandType = System.Data.CommandType.StoredProcedure;
                command.Parameters.Add(new SqlParameter("@field_id", fieldID));
                return Int64.Parse(command.ExecuteScalar().ToString());
            }
        }

        #endregion

    }
}
